www.gusucode.com > VC++ SkinCtrls窗体常用控件换肤程序-源码程序 > VC++ SkinCtrls窗体常用控件换肤程序-源码程序/code/SkinWindows/SkinCtrls.cpp

    // SkinCtrls.cpp : implementation file
// Download by http://www.NewXing.com

#include "stdafx.h"
#include "SkinCtrls.h"
#include "skinctrlmgr.h"
#include "..\shared\wclassdefines.h"
#include "..\shared\winclasses.h"
#include "..\shared\roundcorner.h"

//#define ACTIVATE_VIEWER
//#include "imageviewer.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// globals -- no other way using windows hooks.
static CSkinCtrl*	g_pCtrl = NULL;
static HHOOK		g_hMsgHook = NULL;
static int			g_nScrollbar = -1;

const int CORNER = 4;
const int EDGE = 1;
const int SCROLLCX = ::GetSystemMetrics(SM_CXHSCROLL);

//#define RC_THIN RC_NORMAL

/////////////////////////////////////////////////////////////////////////////
// CSkinCtrls

BOOL CSkinTreeview::AttachWindow(CWnd* pCtrl)
{
	if (!(pCtrl->GetExStyle() & WS_EX_CLIENTEDGE))
		m_dwBaseStyle &= ~SKCS_EDGEMASK;

	return CSkinCtrl::AttachWindow(pCtrl);
}
/*
BOOL CSkinTreeview::OnPaint(CDC* pDC)
{
	if (GetStyle() & TVS_HASBUTTONS)
	{
		CTreeCtrl* pTree = GetTree();

		COLORREF crBack = GetSysColor(COLOR_WINDOW);
		int nIndent = pTree->GetIndent();
		HTREEITEM htItem = pTree->GetFirstVisibleItem();

		CImageList* pILNormal = pTree->GetImageList(TVSIL_NORMAL);
		CImageList* pILState = pTree->GetImageList(TVSIL_STATE);

		while (htItem)
		{
			if (pTree->ItemHasChildren(htItem))
			{
				CRect rButton = GetItemButtonRect(htItem, nIndent, pILNormal, pILState);
				
				// draw the arrow
				BOOL bExpanded = (pTree->GetItemState(htItem, TVIS_EXPANDED) & TVIS_EXPANDED);
				int nOffset = (rButton.Width() - 9) / 2;
				CPoint ptTopLeft = rButton.TopLeft();
				ptTopLeft.Offset(nOffset, nOffset);
				
				DrawItemButton(pDC, rButton, !bExpanded);
				
				pDC->ExcludeClipRect(rButton);
			}
			htItem = pTree->GetNextVisibleItem(htItem);
		}
	}

	return FALSE; // allow default drawing also
}

CRect CSkinTreeview::GetItemButtonRect(HTREEITEM htItem, int nIndent, CImageList* pILNormal, CImageList* pILState)
{
	const float FUDGE_FACTOR = 0.612f;

	CTreeCtrl* pTree = GetTree();
	CRect rItem;
	pTree->GetItemRect(htItem, rItem, TRUE);
				
	// offset for normal image
	if (pILNormal)
	{
		int nImage, nSel;
		
		if (pTree->GetItemImage(htItem, nImage, nSel))
		{
			int cx, cy;
			ImageList_GetIconSize(pILNormal->m_hImageList, &cx, &cy);
			rItem.OffsetRect(-(cx + nIndent / 2 - 13 + (32 - cx) / 2), 0);		
		}
	}
				
	// offset for state image
	if (pILState)
	{
		int nState = (pTree->GetItemState(htItem, LVIS_STATEIMAGEMASK) & LVIS_STATEIMAGEMASK);
		int nImage = nState >> 12;
					
		if (nImage)
		{
			int cx, cy;
			ImageList_GetIconSize(pILState->m_hImageList, &cx, &cy);
			rItem.OffsetRect(-cx, 0);
		}
	}
				
	// rItem.left is now the centre of the button
	rItem.OffsetRect(-(nIndent + 1) / 2, 0);
				
	int nButtonWidth = (int)(rItem.Height() * FUDGE_FACTOR);
				
	rItem.left -= (nButtonWidth) / 2;
	rItem.right = rItem.left + nButtonWidth;
	rItem.DeflateRect(0, (rItem.Height() - nButtonWidth) / 2);
	rItem.OffsetRect(0, 1);

	rItem.bottom = rItem.top + rItem.Width();

	return rItem;
}


void CSkinTreeview::DrawItemButton(CDC* pDC, CRect rButton, BOOL bPlus)
{
	COLORREF crLine = GetColor(COLOR_3DDKSHADOW);
	COLORREF crBorder = GetColor(COLOR_3DSHADOW);
	
	if (GetControlBitmap(SKCB_TREEPLUS, IM_HOT, NULL, SKCB_TREEALL))
	{
		COLORREF crMask;

		CRect rScreen(rButton);
		GetTree()->ClientToScreen(rScreen);

		int nState = IsHot(rScreen, TRUE) ? IM_HOT : IM_COLD;
		CBitmap* pButton = GetControlBitmap(bPlus ? SKCB_TREEPLUS : SKCB_TREEMINUS, nState, &crMask, SKCB_TREEALL);

		BITMAP bm;
		pButton->GetBitmap(&bm);

		CDC dcMem;
		dcMem.CreateCompatibleDC(pDC);
		CBitmap *pOldBitmap = dcMem.SelectObject(pButton);

		CSkinBase::StretchBlt(pDC, rButton.left, rButton.top, rButton.Width(), rButton.Height(),
								&dcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY, crMask);
		
		dcMem.SelectObject(pOldBitmap);
	}
	else // manual
	{
		// border
		pDC->FillSolidRect(rButton.left + 1, rButton.top, rButton.Width() - 2, 1, crBorder);
		pDC->FillSolidRect(rButton.left, rButton.top + 1, 1, rButton.Height() - 2, crBorder);
		pDC->FillSolidRect(rButton.right  - 1, rButton.top + 1, 1, rButton.Height() - 2, crBorder);
		pDC->FillSolidRect(rButton.left + 1, rButton.bottom - 1, rButton.Width() - 2, 1, crBorder);
	}

	if (!GetControlBitmap(SKCB_TREEPLUS))
	{
		CPoint ptCenter = rButton.CenterPoint();

		// horz line
		pDC->FillSolidRect(rButton.left + 2, ptCenter.y, rButton.Width() - 4, 1, crLine);

		if (bPlus) // vert line
			pDC->FillSolidRect(ptCenter.x, rButton.top + 2, 1, rButton.Height() - 4, crLine);
	}
}
*/
void CSkinTreeview::OnSetIni()
{
	CSkinCtrl::OnSetIni();

	GetTree()->SetBkColor(GetColor(COLOR_WINDOW));
}

LRESULT CSkinTreeview::OnCustomDraw(NMCUSTOMDRAW* pNMCD, DWORD dwStage)
{
	switch (dwStage)
	{
	case CDDS_ITEMPREPAINT:
		{
			CDC* pDC = CDC::FromHandle(pNMCD->hdc);
			NMTVCUSTOMDRAW* pTVCD = (NMTVCUSTOMDRAW*)pNMCD;

			BOOL bSelected = (pNMCD->uItemState & CDIS_SELECTED);

			if (bSelected)
				return CDRF_DODEFAULT;

			// else
			pTVCD->clrText = GetColor(COLOR_WINDOWTEXT);
			pTVCD->clrTextBk = GetBkgndColor();

			return CDRF_NEWFONT;
		}
	}
	// default
	return CDRF_DODEFAULT;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinListview::AttachWindow(CWnd* pCtrl)
{
	if (!(pCtrl->GetExStyle() & WS_EX_CLIENTEDGE))
		m_dwBaseStyle &= ~SKCS_EDGEMASK;

	return CSkinCtrl::AttachWindow(pCtrl);
}

LRESULT CSkinListview::OnCustomDraw(NMCUSTOMDRAW* pNMCD, DWORD dwStage)
{
	switch (dwStage)
	{
	case CDDS_ITEMPREPAINT:
		{
			CDC* pDC = CDC::FromHandle(pNMCD->hdc);
			NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)pNMCD;

			pLVCD->clrText = GetColor(COLOR_WINDOWTEXT);
			pLVCD->clrTextBk = GetBkgndColor();
			pDC->SetBkColor(GetBkgndColor());

			return CDRF_NEWFONT;
		}
	}
	// default
	return CDRF_DODEFAULT;
}

void CSkinListview::OnSetIni()
{
	CSkinCtrl::OnSetIni();

	GetList()->SetBkColor(GetColor(COLOR_WINDOW));
	GetList()->SetTextBkColor(GetColor(COLOR_WINDOW));
	GetList()->SetTextColor(GetColor(COLOR_WINDOWTEXT));
}

void CSkinListview::OnStyleChanged(BOOL bExStyle, DWORD dwOldStyle, DWORD dwNewStyle)
{
	if (!bExStyle)
	{
		OnSetIni();

		if ((dwNewStyle & LVS_TYPEMASK) == LVS_REPORT)
		{
			CWnd* pChild = GetChildWnd(WC_HEADER);
			
			if (pChild && !GetSkinCtrl(pChild))
				Skin(*pChild);
		}
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinIPaddress::OnPaint(CDC* pDC)
{
	// draw a dot between each edit box
	CWnd* pEdit1 = GetChildWnd(WC_EDIT);
	ASSERT (pEdit1);

	if (pEdit1)
	{
		CWnd* pEdit2 = pEdit1->GetNextWindow();
		ASSERT (pEdit2);

		CWnd* pEdit3 = pEdit2->GetNextWindow();
		ASSERT (pEdit3);

		CWnd* pEdit4 = pEdit3->GetNextWindow();
		ASSERT (pEdit4);

		CRect r1, r2, r3, r4;

		((CEdit*)pEdit1)->GetRect(r1);
		((CEdit*)pEdit2)->GetRect(r2);
		((CEdit*)pEdit3)->GetRect(r3);
		((CEdit*)pEdit4)->GetRect(r4);

		CWnd* pThis = GetCWnd();

		pEdit1->MapWindowPoints(pThis, r1);
		pEdit2->MapWindowPoints(pThis, r2);
		pEdit3->MapWindowPoints(pThis, r3);
		pEdit4->MapWindowPoints(pThis, r4);

		COLORREF crDot = GetColor(COLOR_WINDOWTEXT);
		pDC->SetPixelV((r1.right + r2.left) / 2, r1.bottom - 2, crDot);
		pDC->SetPixelV((r2.right + r3.left) / 2, r1.bottom - 2, crDot);
		pDC->SetPixelV((r3.right + r4.left) / 2, r1.bottom - 2, crDot);
	}

	return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinEdit::AttachWindow(CWnd* pCtrl)
{
	if (!(pCtrl->GetExStyle() & WS_EX_CLIENTEDGE))
		m_dwBaseStyle &= ~SKCS_EDGEMASK;

	return CSkinCtrl::AttachWindow(pCtrl);
}

LRESULT CSkinEdit::OnMsg(UINT msg, WPARAM wp, LPARAM lp)
{
	switch (msg)
	{
		case WM_KEYDOWN:
			if (wp == VK_BACK || wp == VK_DELETE)
				Invalidate();

			else if (wp == VK_UP || wp == VK_DOWN || wp == VK_LEFT || wp == VK_RIGHT || 
				wp == VK_PRIOR || wp == VK_NEXT || wp == VK_END || wp == VK_HOME)
			{
				Default();
				Refresh(TRUE);
				return 0;
			}
			break;

		case WM_KEYUP:
			if (wp == VK_UP || wp == VK_DOWN || wp == VK_LEFT || wp == VK_RIGHT || 
				wp == VK_PRIOR || wp == VK_NEXT || wp == VK_END || wp == VK_HOME)
			{
				Default();
				Refresh(TRUE);
				return 0;
			}
			break;

		case WM_MOUSEWHEEL:
			Default();
			Refresh(TRUE);
			return 0;

		case WM_VSCROLL:
		case WM_HSCROLL:
			Default();
			Refresh(TRUE);
			return 0;
	}

	return CSkinCtrl::OnMsg(msg, wp, lp);
}

COLORREF CSkinEdit::GetBkgndColor() 
{ 
	if (!IsWindowEnabled() || (GetStyle() & ES_READONLY))
		return GetColor(COLOR_3DFACE);
	
	// else
	return CSkinCtrl::GetBkgndColor();
}

void CSkinEdit::OnNotifyReflect(UINT uNotify, LRESULT lrParent)
{
	switch (uNotify)
	{
		case EN_UPDATE:
			SendMessage(WM_NCPAINT);
			break;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinRichedit::AttachWindow(CWnd* pCtrl)
{
	if (CSkinEdit::AttachWindow(pCtrl))
	{
		// otherwise we don't receive EN_UPDATEs
		DWORD dwEventMask = pCtrl->SendMessage(EM_GETEVENTMASK);
		dwEventMask |= ENM_UPDATE;
		pCtrl->SendMessage(EM_SETEVENTMASK, 0, dwEventMask);
		
		return TRUE;
	}
	
	return FALSE;
}

LRESULT CSkinRichedit::OnMsg(UINT msg, WPARAM wp, LPARAM lp)
{
	switch (msg)
	{
		case WM_MOUSEWHEEL:
		case WM_KEYUP:
		case WM_KEYDOWN:
		case WM_VSCROLL:
		case WM_HSCROLL:
			Default(); // bypass CSkinEdit
			return 0;
	}

	return CSkinEdit::OnMsg(msg, wp, lp);
}

void CSkinRichedit::OnSetIni()
{
	CSkinEdit::OnSetIni();

	GetRichEdit()->SetBackgroundColor(FALSE, GetColor(COLOR_WINDOW));
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinCombobox::AttachWindow(CWnd* pCtrl)
{
	DWORD dwStyle = pCtrl->GetStyle();
	int nType = (dwStyle & 0xf);
	
	// droplist drawing can only be well done via ownerdraw
	if (nType == CBS_DROPDOWNLIST)
	{
		if (dwStyle & CBS_OWNERDRAWFIXED || dwStyle & CBS_OWNERDRAWVARIABLE)
			m_bIsOwnerdraw = TRUE;
/*		else
		{
			pCtrl->ModifyStyle(0, CBS_OWNERDRAWVARIABLE | CBS_HASSTRINGS);
			m_dwBaseStyle |= SKCS_DRAWITEM;
		}
*/	}
	
	return CSkinCtrl::AttachWindow(pCtrl);
}

void CSkinCombobox::OnNcCalcSize(LPRECT pClient)
{
	::InflateRect(pClient, -EDGE, 0);
	
	if ((GetStyle() & 0xf) != CBS_SIMPLE)
		pClient->right -= EDGE;
	
	else
		pClient->right += EDGE;
}

BOOL CSkinCombobox::OnPaint(CDC* pDC)
{
	int nType = (GetStyle() & 0xf);

	if (nType == CBS_DROPDOWN)
		return TRUE; // edit box draws

	CComboBox* pCombo = GetCombo();
	CRect rClient;
	GetClientRect(rClient);

	CRect rPaint(rClient);
	rPaint.DeflateRect(3, 3);
	rClient.InflateRect(5, 5);

	BOOL bEndPaint = FALSE;

	if (nType == CBS_SIMPLE)
	{
		// also exclude the divider between the edit and listbox
		CWnd* pLBox = GetChildWnd(WC_COMBOLBOX);
		
		if (pLBox)
		{
			CRect rLBox;
			pLBox->GetWindowRect(rLBox);
			pLBox->ScreenToClient(rLBox);
			pLBox->MapWindowPoints(pCombo, rLBox);

			pDC->ExcludeClipRect(rClient.left, rLBox.top, rClient.right, EDGE * 2);
		}
	}
	else if (!m_bIsOwnerdraw)
	{
		// all we do is paint the current selected item
		CComboBox* pCombo = GetCombo();
		BOOL bFocus = !pCombo->GetDroppedState() && CWnd::GetFocus() == pCombo;
		BOOL bEnabled = IsWindowEnabled();

		COLORREF crBack = bFocus ? GetColor(COLOR_HIGHLIGHT) : GetBkgndColor();
		COLORREF crText = GetColor(bFocus ? COLOR_HIGHLIGHTTEXT : bEnabled ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);

		pDC->SelectStockObject(ANSI_VAR_FONT);
		pDC->SetBkColor(crBack);
		pDC->SetTextColor(crText);
		pDC->SetBkMode(TRANSPARENT);
		
		CString sText;
		pCombo->GetWindowText(sText);

		rPaint.InflateRect(1, 1);
		rPaint.right -= SCROLLCX - 3;
		pDC->FillSolidRect(rPaint, GetBkgndColor());
		rPaint.DeflateRect(0, 1);
		
		if (bFocus)
		{
			pDC->FillSolidRect(rPaint, crBack);
			rPaint.right--;
			pDC->DrawFocusRect(rPaint);
		}

		rPaint.OffsetRect(1, -1);
		pDC->DrawText(sText, rPaint, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
		
		// finally redraw button
		CRect rButton;
		GetClientRect(rButton);
		DrawDropButton(pDC, rButton);

		bEndPaint = TRUE;
	}
	else
		rPaint.right -= SCROLLCX - 3;

	pDC->ExcludeClipRect(rClient.left, rClient.top, rClient.right, rPaint.top);
	pDC->ExcludeClipRect(rClient.left, rClient.top, rPaint.left, rClient.bottom);
	pDC->ExcludeClipRect(rPaint.right, rClient.top, rClient.right, rClient.bottom);
	pDC->ExcludeClipRect(rClient.left, rPaint.bottom, rClient.right, rClient.bottom);

	return bEndPaint;
}

void CSkinCombobox::DrawDropButton(CDC* pDC, CRect& rClient, LPPOINT ptCursor)
{
	rClient.top += (rClient.Height() - SCROLLCX) / 2;
	rClient.bottom = rClient.top + SCROLLCX;
	rClient.left = rClient.right - SCROLLCX;
	
	CSkinCtrl::DrawDropButton(pDC, rClient, ptCursor, GetBkgndColor());
}

void CSkinCombobox::OnSetFocus(BOOL bSet)
{
	if (!bSet) // kill focus
	{
		CWindowDC dc(GetCWnd());
		CRect rClient;
		GetDrawRect(NULL, rClient);
		DrawDropButton(&dc, rClient);
	}

	if (!m_bIsOwnerdraw)
		Invalidate(FALSE);
}

void CSkinCombobox::OnNcPaint(CDC* pDC)
{
	if ((GetStyle() & 0xf) != CBS_SIMPLE)
	{
		CSkinCtrl::OnNcPaint(pDC);
		
		CRect rClient;
		GetDrawRect(NULL, rClient);
		DrawDropButton(pDC, rClient);
	}
	else
	{
		CWnd* pLBox = GetChildWnd(WC_COMBOLBOX);
		
		if (pLBox)
		{
			CRect rLBox, rWindow;
			GetDrawRect(rWindow);
			
			pLBox->GetWindowRect(rLBox);
			pLBox->ScreenToClient(rLBox);
			pLBox->MapWindowPoints(GetCWnd(), rLBox);
			
			// paint a thin border around the combolbox
			pDC->FillSolidRect(rWindow.left, rLBox.top, EDGE, rLBox.Height(), GetParentBkgndColor());
			pDC->FillSolidRect(rWindow.left, rLBox.top - EDGE, rWindow.Width(), EDGE, GetParentBkgndColor());
			
			// then border the edit box
			rWindow.bottom = rLBox.top - 1;
			
			NcPaintBorder(pDC, rWindow, GetParentBkgndColor(), GetBkgndColor(), EdgeStyle());
		}
	}
}

void CSkinCombobox::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	CSkinCtrl::OnUpdateHotspots(pOldCursor, pNewCursor, uOldHitTest, uNewHitTest);

	if ((GetStyle() & 0xf) == CBS_SIMPLE)
		return;

	CRect rButton;
	GetDrawRect(NULL, rButton);
	rButton.top += (rButton.Height() - SCROLLCX) / 2;
	rButton.bottom = rButton.top + SCROLLCX;
	rButton.left = rButton.right - SCROLLCX;

	BOOL bOn = FALSE, bWasOn = FALSE;

	if (pOldCursor && pNewCursor)
	{
		bOn = rButton.PtInRect(*pNewCursor);
		bWasOn = rButton.PtInRect(*pOldCursor);
	}
	else if (pOldCursor)
		bWasOn = rButton.PtInRect(*pOldCursor);

	else if (pNewCursor)
		bOn = rButton.PtInRect(*pNewCursor);

	if (bOn != bWasOn)
	{
		CWindowDC dc(GetCWnd());
		CSkinCtrl::DrawDropButton(&dc, rButton, NULL, GetBkgndColor());
	}
}

COLORREF CSkinCombobox::GetBkgndColor()
{
	if (IsWindowEnabled())
		return m_bIsOwnerdraw ? ::GetSysColor(COLOR_WINDOW) : GetColor(COLOR_WINDOW);
	else
		return m_bIsOwnerdraw ? ::GetSysColor(COLOR_3DFACE) : GetColor(COLOR_3DFACE);
}

BOOL CSkinCombobox::OnEraseBkgnd(CDC* pDC)
{
	CRect rClient;
	GetClientRect(rClient);
	
	if ((GetStyle() & 0xf) != CBS_SIMPLE)
	{
		CRect rButton = rClient;
		DrawDropButton(pDC, rButton);
		pDC->ExcludeClipRect(rButton);
	}
	else
	{
		CWnd* pLBox = GetChildWnd(WC_COMBOLBOX);
		
		if (pLBox)
		{
			CRect rLBox;
			pLBox->GetWindowRect(rLBox);
			pLBox->ScreenToClient(rLBox);
			pLBox->MapWindowPoints(GetCWnd(), rLBox);

			rClient.bottom = rLBox.top - 1;
		}
	}
	
	rClient.DeflateRect(2, 2);
	rClient.right--;
	pDC->FillSolidRect(rClient, GetBkgndColor());
	
	return TRUE;
}

void CSkinCombobox::OnNotifyReflect(UINT uNotify, LRESULT lrParent)
{
	switch (uNotify)
	{
		case CBN_DROPDOWN:
		case CBN_CLOSEUP:
			{
				CWindowDC dc(GetCWnd());
				CRect rClient;
				GetDrawRect(NULL, rClient);
				DrawDropButton(&dc, rClient);

				DelayRedraw(200);
			}
			break;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinUpdown::OnPaint(CDC* pDC)
{
	CRect rClient;
	GetClientRect(rClient);

	if (CWinClasses::IsClass(::GetParent(GetHwnd()), WC_DATETIMEPICK))
	{
		rClient.bottom--;
		rClient.right--;
	}

	DrawSpinButton(pDC, rClient, NULL, !(GetStyle() & UDS_HORZ), GetParentBkgndColor());
	return TRUE;
}

void CSkinUpdown::OnNcPaint(CDC* pDC)
{
	CRect rWindow;
	GetDrawRect(rWindow);

	if (CWinClasses::IsClass(::GetParent(GetHwnd()), WC_DATETIMEPICK))
	{
		rWindow.bottom--;
		rWindow.right--;
	}

	DrawSpinButton(pDC, rWindow, NULL, !(GetStyle() & UDS_HORZ), GetParentBkgndColor());
}

void CSkinUpdown::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	// if its move on or off then its a simple redraw
	if (!pOldCursor || !pNewCursor)
		Invalidate(FALSE);

	else
	{
		CRect rWindow, rBack, rForward;
		GetDrawRect(rWindow);

		rBack = rForward = rWindow;

		if (GetStyle() & UDS_HORZ)
		{
			rBack.right = (rBack.right + rBack.left) / 2;
			rForward.left = rBack.right;
		}
		else
		{
			rBack.bottom = (rBack.top + rBack.bottom) / 2;
			rForward.top = rBack.bottom;
		}

		BOOL bOn = rBack.PtInRect(*pNewCursor);
		BOOL bWasOn = rBack.PtInRect(*pOldCursor);

		if (bOn != bWasOn)
		{
			Invalidate(FALSE);
			return;
		}

		bOn = rForward.PtInRect(*pNewCursor);
		bWasOn = rForward.PtInRect(*pOldCursor);

		if (bOn != bWasOn)
		{
			Invalidate(FALSE);
			return;
		}
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinScrollbar::AttachWindow(CWnd* pCtrl)
{
	DWORD dwStyle = pCtrl->GetStyle();

	if ((dwStyle & SBS_SIZEBOX) || (dwStyle & SBS_SIZEGRIP))
	{
		if (CSkinCtrl::AttachWindow(pCtrl))
		{
			m_dwBaseStyle |= SKCS_PAINT | SKCS_ERASEBKGND | SKCS_NCPAINT;
			return TRUE;
		}
	}

	return FALSE;
}

BOOL CSkinScrollbar::OnPaint(CDC* pDC)
{
	return TRUE;
}

void CSkinScrollbar::OnNcPaint(CDC* pDC)
{
}


//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinHotkey::OnPaint(CDC* pDC)
{
/*	// all we do is paint the current selected item
	CComboBox* pCombo = GetCombo();
	BOOL bFocus = !pCombo->GetDroppedState() && CWnd::GetFocus() == pCombo;
	COLORREF crBack = bFocus ? GetColor(COLOR_HIGHLIGHT) : GetBkgndColor();
	COLORREF crText = bFocus ? GetColor(COLOR_HIGHLIGHTTEXT) : GetColor(COLOR_WINDOWTEXT);
	pDC->SelectStockObject(ANSI_VAR_FONT);
	pDC->SetBkColor(crBack);
	pDC->SetTextColor(crText);
	
	CString sText;
	pCombo->GetWindowText(sText);
	
	CRect rClient;
	GetClientRect(rClient);
	rClient.right -= SCROLLCX - 3;
	rClient.DeflateRect(3, 3);
	
	pDC->ExtTextOut(rClient.left + 1, rClient.top + 1, ETO_CLIPPED | ETO_OPAQUE, rClient, sText, NULL);
*/	
	return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinListbox::AttachWindow(CWnd* pCtrl)
{
	DWORD dwStyle = pCtrl->GetStyle();
	
	if (!(pCtrl->GetExStyle() & WS_EX_CLIENTEDGE))
		m_dwBaseStyle &= ~SKCS_EDGEMASK;

	// we don't support LBS_DISABLENOSCROLL at present
	if (pCtrl->GetStyle() & LBS_DISABLENOSCROLL)
	{
		TRACE("*** CSkinListbox cannot support LBS_DISABLENOSCROLL. must be removed in dialog template or elsewhere prior to creation time");
		return FALSE;
	}

	return CSkinCtrl::AttachWindow(pCtrl);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinStatic::AttachWindow(CWnd* pCtrl)
{
	DWORD dwStyle = pCtrl->GetStyle();
	int nType = (dwStyle & 0x1F);
	
	switch (nType)
	{
		case SS_LEFT:
		case SS_RIGHT:
		case SS_CENTER:
		case SS_SIMPLE:
		case SS_LEFTNOWORDWRAP:
			m_dwBaseStyle |= SKCS_NCPAINT;
			break;

		case SS_ETCHEDHORZ:       
		case SS_ETCHEDVERT:       
		case SS_ETCHEDFRAME: 
			m_dwBaseStyle |= SKCS_PAINT | SKCS_NCPAINT | SKCS_NOBASENCPAINT | SKCS_ETCHEDEDGE;
			break;

		case SS_ICON:
			m_dwBaseStyle |= SKCS_NOEDGE | SKCS_PAINT | SKCS_NOBASENCPAINT;

		default:
			m_dwBaseStyle |= SKCS_PAINT | SKCS_NCPAINT | SKCS_NOBASENCPAINT;
			break;
	}

	return CSkinCtrl::AttachWindow(pCtrl);
}

COLORREF CSkinStatic::GetBkgndColor() 
{
	DWORD dwStyle = GetStyle();

	if (dwStyle & SS_SUNKEN)
	{
		int nType = (dwStyle & 0x1F);

		switch (nType)
		{
			case SS_BLACKRECT: 
				return GetColor(COLOR_3DDKSHADOW);

			case SS_GRAYRECT:         
				return GetColor(COLOR_3DSHADOW);

			case SS_WHITERECT:   
				return GetColor(COLOR_3DHIGHLIGHT);

			case SS_ETCHEDHORZ:       
			case SS_ETCHEDVERT:       
			case SS_ETCHEDFRAME: 
				return GetColor(COLOR_3DFACE);
		}
	}
	
	return GetParentBkgndColor(); 
}

LRESULT CSkinStatic::OnMsg(UINT msg, WPARAM wp, LPARAM lp)
{
	switch (msg)
	{
		case WM_PAINT:
			{
				DWORD dwStyle = GetStyle();

				if ((dwStyle & SS_SUNKEN)/* && (dwStyle & 0x1F) == SS_ICON*/)
				{
					LRESULT lr = CSkinCtrl::OnMsg(msg, wp, lp);

					CWindowDC dc(GetCWnd());
					CRect rect;
					GetDrawRect(rect);
					
					if (!s_pRenderer)
						FillCorners(&dc, rect, GetParentBkgndColor());

					Draw3dEdge(&dc, rect, CORNER, SKCS_CLIENTEDGE, IsWindowEnabled() ? IM_COLD : IM_DISABLED);

					return lr;
				}
				break;
			}
	}

	return CSkinCtrl::OnMsg(msg, wp, lp);
}

BOOL CSkinStatic::OnPaint(CDC* pDC)
{
	int nType = (GetStyle() & 0x1F);

	switch (nType)
	{
		case SS_BLACKRECT: 
		case SS_GRAYRECT:         
		case SS_WHITERECT:   
			{
				CRect rClient;
				GetClientRect(rClient);

				pDC->FillSolidRect(rClient, GetBkgndColor());
				return TRUE;
			}

		case SS_BLACKFRAME: 
		case SS_GRAYFRAME:         
		case SS_WHITEFRAME:   
		case SS_ETCHEDHORZ:       
		case SS_ETCHEDVERT:       
		case SS_ETCHEDFRAME: 
			return TRUE;
	}

	return FALSE;
}

void CSkinStatic::OnNcPaint(CDC* pDC)
{
	CSkinCtrl::OnNcPaint(pDC);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinProgress::OnPaint(CDC* pDC)
{
	CRect rClient;
	GetClientRect(rClient);
	rClient.DeflateRect(1, 1);
	
	BOOL bSmooth = (GetStyle() & PBS_SMOOTH);
	BOOL bHorz = !(GetStyle() & PBS_VERTICAL);

	int nPos= GetProgress()->GetPos();
	
	int nLower, nUpper;
	GetProgress()->GetRange(nLower, nUpper);

	if (bHorz)
		rClient.right = rClient.left + (rClient.Width() * nPos) / (nUpper - nLower);
	else
		rClient.top = rClient.bottom - (rClient.Height() * nPos) / (nUpper - nLower);

	if (GetControlBitmap(SKCB_PROGRESSLEFT))
	{
		COLORREF crMask;
		CBitmap* pLeft = GetControlBitmap(SKCB_PROGRESSLEFT, IM_HOT, &crMask);
		CBitmap* pRight = GetControlBitmap(SKCB_PROGRESSRIGHT, IM_HOT, &crMask);
		CBitmap* pMid = GetControlBitmap(SKCB_PROGRESSMIDDLE, IM_HOT, &crMask);

		DrawPushButton(pDC, rClient, pLeft, pMid, pRight, GetBkgndColor(), crMask);
	}
	else
	{
		CPen* pOldPen = pDC->SelectObject(GetColorPen(COLOR_HIGHLIGHT));
		CBrush* pOldBr = pDC->SelectObject(GetColorBrush(COLOR_HIGHLIGHT));
		
		if (bSmooth)
			pDC->RoundRect(rClient, CPoint(CORNER * 2, CORNER * 2));
		
		else
		{
			// the standard bar element is 2/3 the client breadth
			const int DIVIDER = 2;
			CRect rBar(rClient);

			if (bHorz)
			{
				int nWidth = (rClient.Height() * 2) / 3;
				rBar.right = min(rClient.right, rBar.left + nWidth);
				
				while (rBar.right <= rClient.right)
				{
					pDC->RoundRect(rBar, CPoint(CORNER * 2, CORNER * 2));
					rBar.OffsetRect(DIVIDER + nWidth, 0);
				}
			}
			else
			{
				int nHeight = (rClient.Width() * 2) / 3;
				rBar.top = max(rClient.top, rBar.bottom - nHeight);
				
				while (rBar.top >= rClient.top)
				{
					pDC->RoundRect(rBar, CPoint(CORNER * 2, CORNER * 2));
					rBar.OffsetRect(0, -(DIVIDER + nHeight));
				}
			}
		}
	
		pDC->SelectObject(pOldBr);
		pDC->SelectObject(pOldPen);
	}
	
	return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinDatetimepick::s_bDropDown = FALSE;
const UINT TIMER_DROP = 1;

void CSkinDatetimepick::OnNcCalcSize(LPRECT pClient)
{
	::InflateRect(pClient, -EDGE, -EDGE);
}

void CSkinDatetimepick::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	CSkinCtrl::OnUpdateHotspots(pOldCursor, pNewCursor, uOldHitTest, uNewHitTest);

	if (GetStyle() & DTS_UPDOWN)
		return;

	CRect rClient;
	GetDrawRect(NULL, rClient);
	rClient.InflateRect(0, EDGE);

	CRect rButton = rClient;
	rButton.top += (rButton.Height() - SCROLLCX) / 2;
	rButton.bottom = rButton.top + SCROLLCX;
	rButton.left = rButton.right - SCROLLCX;

	BOOL bOn = FALSE, bWasOn = FALSE;
	
	// if its move on or off then its a simple redraw
	if (pOldCursor && pNewCursor)
	{
		bOn = rButton.PtInRect(*pNewCursor);
		bWasOn = rButton.PtInRect(*pOldCursor);
	}
	else if (pOldCursor)
		bWasOn = rButton.PtInRect(*pOldCursor);

	else if (pNewCursor)
		bOn = rButton.PtInRect(*pNewCursor);

	if (bOn != bWasOn)
	{
		CWindowDC dc(GetCWnd());
		DrawDropButton(&dc, rClient);
	}
}

LRESULT CSkinDatetimepick::OnNotifyReflect(NMHDR* pNMHDR, LRESULT lrParent)
{
	switch (pNMHDR->code)
	{
	case DTN_DROPDOWN:
		s_bDropDown = TRUE;
		SendMessage(WM_NCPAINT);
		GetCtrl()->SetRedraw(FALSE);
		GetCtrl()->SetTimer(TIMER_DROP, 10, NULL);
		break;
		
	case DTN_CLOSEUP:
		s_bDropDown = FALSE;
		GetCtrl()->SetRedraw(TRUE);
		GetCtrl()->KillTimer(TIMER_DROP);
		DelayRedraw(200);
		break;
		
	case DTN_DATETIMECHANGE:
		Invalidate(FALSE);
		break;
	}
	
	return 0;
}

void CSkinDatetimepick::OnTimer(UINT nIDEvent)
{
	if (nIDEvent == TIMER_DROP)
	{
		if (s_bDropDown)
		{
			GetCtrl()->SetRedraw(TRUE);
			
			CWindowDC dc(GetCWnd());
			CRect rClient;
			GetDrawRect(NULL, rClient);
			rClient.InflateRect(0, EDGE);
			DrawDropButton(&dc, rClient);
		}
		else
		{
			GetCtrl()->KillTimer(TIMER_DROP);
			GetCtrl()->SetRedraw(TRUE);
		}
	}
}

void CSkinDatetimepick::DrawDropButton(CDC* pDC, CRect& rClient, LPPOINT ptCursor)
{
	CRect rButton(rClient);
	rButton.left = rClient.right - SCROLLCX;

	if (rButton.Height() > SCROLLCX)
	{
		int nClip = max(0, (rButton.Height() - SCROLLCX) / 2);

		rButton.top += nClip;
		rButton.bottom = rButton.top + SCROLLCX;
		
		// paint over drop button first above and below the 
		pDC->FillSolidRect(rButton.left, rClient.top, rClient.right - rButton.left, nClip, GetBkgndColor());
		pDC->FillSolidRect(rButton.left, rButton.bottom, rClient.right - rButton.left, rClient.bottom - rButton.bottom, GetBkgndColor());
	}

	CSkinCtrl::DrawDropButton(pDC, rButton, ptCursor, GetBkgndColor());

	rClient = rButton;
}

BOOL CSkinDatetimepick::OnPaint(CDC* pDC)
{
	// ideally we could use the same approach as the combobox
	// except it doesn't work because the dropdown button gets 
	// drawn without reference to the clip rect
	
	// all we do is paint the current selected item
	CDateTimeCtrl* pPicker = GetPicker();
	
	BOOL bFocus = !s_bDropDown && (CWnd::GetFocus() == pPicker);
	BOOL bEnabled = IsWindowEnabled();
	
	pDC->SelectStockObject(ANSI_VAR_FONT);
	
	CString sText;
	pPicker->GetWindowText(sText);
	
	CRect rClient, rText;
	GetClientRect(rClient);
		
	rClient.right -= SCROLLCX;
	pDC->FillSolidRect(rClient, bFocus ? GetColor(COLOR_HIGHLIGHT) : GetBkgndColor());

	rText = rClient;
	CalcTextRect(pDC, sText, rText, DT_LEFT | DT_VCENTER);
	rText.OffsetRect(2, 0);

	pDC->SetTextColor(GetColor(bFocus ? COLOR_HIGHLIGHTTEXT : bEnabled ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT));
	pDC->SetBkMode(TRANSPARENT);
			
	pDC->DrawText(sText, rText, DT_TOP | DT_LEFT | DT_END_ELLIPSIS);
	
	if (bFocus)
		pDC->DrawFocusRect(rClient);
	
	return TRUE;
}

void CSkinDatetimepick::OnNcPaint(CDC* pDC)
{
	CSkinCtrl::OnNcPaint(pDC);

	if (!(GetStyle() & DTS_UPDOWN))
	{
		CRect rClient;
		GetDrawRect(NULL, rClient);
		rClient.InflateRect(0, EDGE);
		DrawDropButton(pDC, rClient);
	}	
}

BOOL CSkinDatetimepick::OnEraseBkgnd(CDC* pDC)
{
	CRect rClient;
	GetClientRect(rClient);
		
	if (!(GetStyle() & DTS_UPDOWN))
	{
		CRect rButton(rClient);
		rButton.InflateRect(0, EDGE);
		DrawDropButton(pDC, rButton);
		
		rButton.top--;
		pDC->ExcludeClipRect(rButton);
	}
	
	rClient.InflateRect(2, 2);
	
	if (CWnd::GetFocus() != GetCWnd())
		pDC->FillSolidRect(rClient, GetBkgndColor());
	
	return TRUE;
}

void CSkinDatetimepick::OnSetFocus(BOOL bSet) 
{
	Invalidate();
}

//////////////////////////////////////////////////////////////////////////////////////////////////

CSkinButton* CSkinButton::NewButton(DWORD dwBtnStyle)
{
	CSkinButton* pButton = NULL;
	int nType = (dwBtnStyle & 0x0f); 

	switch (nType)
	{
		case BS_PUSHBUTTON:       
		case BS_DEFPUSHBUTTON: 
			return new CSkinButtonPush;
			
		case BS_CHECKBOX:         
		case BS_AUTOCHECKBOX:     
		case BS_3STATE:           
		case BS_AUTO3STATE:       
			return new CSkinButtonCheck;

		case BS_RADIOBUTTON:      
		case BS_AUTORADIOBUTTON:  
			return new CSkinButtonRadio;

		case BS_GROUPBOX:     
			return new CSkinButtonGroup;
			
		case BS_OWNERDRAW:        
		case BS_USERBUTTON: 
			return new CSkinButtonPush;

		default:
			return NULL; // let user draw
	}
}

BOOL CSkinButton::AttachWindow(CWnd* pCtrl)
{
	m_nButtonType = (pCtrl->GetStyle() & 0x0f); 
	
	BOOL bRes = CSkinCtrl::AttachWindow(pCtrl);

	// there's something odd going on in the file dialog (amongst others)
	// whereby the text is not rendered correctly perhaps because the text
	// is not present when we first render it.
	if (bRes)
		DelayRedraw(1);

	return bRes;
}

CRect CSkinButton::GetCheckRect()
{
	CRect rect;
	GetClientRect(rect);

	BOOL bRadio = (m_nButtonType == BS_AUTORADIOBUTTON || m_nButtonType == BS_RADIOBUTTON);
	BOOL bCheck = (m_nButtonType == BS_AUTOCHECKBOX || m_nButtonType == BS_CHECKBOX ||
					m_nButtonType == BS_AUTO3STATE || m_nButtonType == BS_3STATE);

	if (bRadio || bCheck)
	{
		UINT nStyle = GetStyle();
		
		if (nStyle & BS_LEFTTEXT)
			rect.left = rect.right - 16;
		else
			rect.right = rect.left + 16;
		
		// fall thru
		if (!(nStyle & BS_VCENTER) || (nStyle & BS_VCENTER) == BS_VCENTER)
		{
			rect.top = (rect.top + rect.bottom - 16) / 2;
			rect.bottom = rect.top + 16;
		}
		else if (nStyle & BS_BOTTOM)
			rect.top = rect.bottom - 16;
		
		else // top
			rect.bottom = rect.top + 16;
		
		if (bCheck)
		{
			rect.DeflateRect(1, 1);
			rect.right--;
			rect.bottom--;
		}
		else
			rect.DeflateRect(2, 2);
	}

	return rect;
}

UINT CSkinButton::GetTextAlignment()
{
	UINT nTextAlign = 0;
	UINT nStyle = GetStyle();

	// vert
	if (!(nStyle & BS_VCENTER))
		nTextAlign |= DT_VCENTER;
		
	else if ((nStyle & BS_VCENTER) == BS_VCENTER)
		nTextAlign |= DT_VCENTER;

	else if (nStyle & BS_BOTTOM)
		nTextAlign |= DT_BOTTOM;

	else // top
		nTextAlign |= DT_TOP;

	// horz
	if (!(nStyle & BS_CENTER))
	{
		if (m_nButtonType == BS_PUSHBUTTON || m_nButtonType == BS_DEFPUSHBUTTON)
			nTextAlign |= DT_CENTER;
		else
			nTextAlign |= DT_LEFT;
	}
	else if ((nStyle & BS_CENTER) == BS_CENTER)
		nTextAlign |= DT_CENTER;

	else if (nStyle & BS_LEFT)
		nTextAlign |= DT_LEFT;

	else // right
		nTextAlign |= DT_RIGHT;

	// lines
	if (!(nStyle & BS_MULTILINE))
		nTextAlign |= DT_SINGLELINE;

	return nTextAlign;
}

LRESULT CSkinButton::OnMsg(UINT msg, WPARAM wp, LPARAM lp)
{
	LRESULT lr = 0;

	switch (msg)
	{
	case WM_SETFOCUS:
	case WM_KILLFOCUS:
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case BM_SETCHECK:
		{
			SetRedraw(FALSE);
			lr = Default();
			SetRedraw(TRUE);

			Invalidate(FALSE);
			UpdateWindow(GetHwnd());
			return lr;
		}

	case BM_SETSTATE:
		{
			BOOL bCurState = (SendMessage(BM_GETSTATE) & BST_PUSHED);

			if ((wp && bCurState) || (!wp && !bCurState))
				break; // no change

			SetRedraw(FALSE);
			lr = Default();
			SetRedraw(TRUE);

			Invalidate(FALSE);
			UpdateWindow(GetHwnd());
			return lr;
		}
	}

	return CSkinCtrl::OnMsg(msg, wp, lp);
}

////

BOOL CSkinButtonPush::AttachWindow(CWnd* pCtrl)
{
	if (CSkinButton::AttachWindow(pCtrl))
	{
		if (m_nButtonType == BS_OWNERDRAW || m_nButtonType == BS_USERBUTTON)
			m_dwBaseStyle |= SKCS_POSTDRAWITEM;

		return TRUE;
	}

	return FALSE;
}

BOOL CSkinButtonPush::OnPaint(CDC* pDC) // return FALSE to allow default painting
{
	// owner draw push buttons handled in OnDrawItem
	if (m_nButtonType == BS_OWNERDRAW || m_nButtonType == BS_USERBUTTON)
		return FALSE;

	CRect rClient;
	GetClientRect(rClient);
	
	int nState = 0;
	BOOL bFocus = FALSE, bDown = FALSE;

	if (!IsWindowEnabled())
		nState = IM_DISABLED;
	else
	{
		UINT uState = GetButton()->GetState();

		bDown = (uState & 0x0004);
		bFocus = (uState & 0x0008);

		nState = bDown ? IM_DOWN : (IsHot() ? IM_HOT : IM_COLD);
	}
	
	Draw(pDC, rClient, nState, bFocus, FALSE);

	return TRUE;
}

void CSkinButtonPush::Draw(CDC* pDC, LPRECT pRect, int nState, BOOL bFocus, BOOL bOwnerdraw)
{
	UINT nTextAlign = GetTextAlignment();
	
	CBitmap bmp;
	CRect rClient(pRect);
	CRect rText(rClient);
	
	bmp.CreateCompatibleBitmap(pDC, rClient.Width(), rClient.Height());
	
	CDC dcMem;
	dcMem.CreateCompatibleDC(pDC);
	CBitmap *pOldBitmap = dcMem.SelectObject(&bmp);
		
	BOOL bClipLeft = HasCtrlStyle(SKBS_CLIPLEFT);
	BOOL bClipRight = HasCtrlStyle(SKBS_CLIPRIGHT);
	
	// bitmaps or std drawn
	if (GetControlBitmap(SKCB_BUTTONLEFT))
	{
		COLORREF crMask;
		CBitmap* pLeft = GetControlBitmap(bClipLeft ? SKCB_BUTTONCLIPLEFT : SKCB_BUTTONLEFT, nState, &crMask, SKCB_BUTTONLEFT);
		CBitmap* pRight = GetControlBitmap(bClipRight ? SKCB_BUTTONCLIPRIGHT : SKCB_BUTTONRIGHT, nState, NULL, SKCB_BUTTONRIGHT);
		CBitmap* pMid = GetControlBitmap(SKCB_BUTTONMIDDLE, nState);

		DrawPushButton(&dcMem, rClient, pLeft, pMid, pRight, GetParentBkgndColor(), crMask);
	}
	else
	{
		int nCorner = (min(rClient.Height(), rClient.Width()) / 2) - CORNER;

		DrawPushButton(&dcMem, rClient, nState, nCorner, GetParentBkgndColor(), bClipLeft, bClipRight);
	}

	// if we are ownerdraw then we transparentblt pDC to our memDC
	if (bOwnerdraw)
	{
		CSkinBase::BitBlt(&dcMem, 4, 4, rClient.Width() - 8, rClient.Height() - 8,
							pDC, 4, 4, SRCCOPY, GetSysColor(COLOR_3DFACE));
	}
	else // else we draw the text, bitmap or icon
	{
		CButton* pBtn = GetButton();
		DWORD dwStyle = pBtn->GetButtonStyle();

		if (dwStyle & (BS_ICON | BS_BITMAP)) // we must do it this way because BS_TEXT == 0
		{
			rText.DeflateRect(CORNER, CORNER);

			if (dwStyle & BS_ICON)
			{
				HICON hIcon = pBtn->GetIcon();

				if (hIcon)
				{
					CRect rIcon(0, 0, 33, 33); // 33 instead of 32 to allow for down state
					AlignRect(rIcon, rText, nTextAlign);

					if (nState == IM_DOWN) 
						rIcon.OffsetRect(1, 1);
					
					dcMem.DrawIcon(rIcon.TopLeft(), hIcon);
				}
			}
			else if (dwStyle & BS_BITMAP)
			{
				CBitmap* pBitmap = CBitmap::FromHandle(pBtn->GetBitmap());

				if (pBitmap)
				{
					BITMAP BM;
					pBitmap->GetBitmap(&BM);

					CRect rBM(0, 0, min(BM.bmWidth, rText.Width()), min(BM.bmWidth, rText.Height()));
					BM.bmWidth++; // to allow for down state
					BM.bmHeight++; // to allow for down state

					AlignRect(rBM, rText, nTextAlign);

					if (nState == IM_DOWN) 
						rBM.OffsetRect(1, 1);
					
					CDC dcBtn;
					dcBtn.CreateCompatibleDC(pDC);
					CBitmap* pOld = dcBtn.SelectObject(pBitmap);

					// use the std light gray color (192, 192, 192) as the transparent color
					CSkinBase::BitBlt(&dcMem, rBM.left, rBM.top, rBM.Width() - 1, rBM.Height() - 1, 
										&dcBtn, 0, 0, SRCCOPY, 0xc0c0c0);

					// cleanup
					dcBtn.SelectObject(pOld);
					dcBtn.DeleteDC();
				}
			}

			// Draw the focus circle on the inside of the button
			if (bFocus)
				dcMem.DrawFocusRect(rText);
		}
		else
		{
			// draw the text if there is any
			CString strText;
			pBtn->GetWindowText(strText);

			dcMem.SelectStockObject(ANSI_VAR_FONT);
			rText.DeflateRect(CORNER, 0);
			CalcTextRect(&dcMem, strText, rText, nTextAlign);
			
			if (nState == IM_DOWN) 
				rText.OffsetRect(1, 1);
			
			if (!strText.IsEmpty())
			{
				dcMem.SetBkMode(TRANSPARENT);
				
				COLORREF colorText = GetColor(IsWindowEnabled() ? COLOR_BTNTEXT : COLOR_GRAYTEXT);
				COLORREF oldcol = dcMem.SetTextColor(colorText);

				dcMem.DrawText(strText, rText, nTextAlign | DT_END_ELLIPSIS);
				dcMem.SetTextColor(oldcol);
				
				rText.InflateRect(2, 2);
			}
			else
				rText.DeflateRect(CORNER, CORNER);
			
			// Draw the focus circle on the inside of the button if it is non-stretched
			if (bFocus)
				dcMem.DrawFocusRect(rText);
		}
	}
	
	// blt to screen
	pDC->BitBlt(0, 0, rClient.right, rClient.bottom, &dcMem, 0, 0, SRCCOPY);
	
	// cleanup
	dcMem.SelectObject(pOldBitmap);
	bmp.DeleteObject();
	dcMem.DeleteDC();
}

BOOL CSkinButtonPush::OnDrawItem(LPDRAWITEMSTRUCT lpDIS)
{
	// sanity check
	if (!(m_nButtonType == BS_OWNERDRAW || m_nButtonType == BS_USERBUTTON))
		return FALSE;

	CDC* pDC = CDC::FromHandle(lpDIS->hDC);

	BOOL bDis = (lpDIS->itemState & ODS_DISABLED);
	BOOL bFocus = (!bDis && (lpDIS->itemState & ODS_FOCUS));
	BOOL bDown = (lpDIS->itemState & ODS_SELECTED);

	int nState = bDis ? IM_DISABLED : (bDown ? IM_DOWN : (IsHot() ? IM_HOT : IM_COLD));

	Draw(pDC, &lpDIS->rcItem, nState, bFocus, TRUE);

	return TRUE;
}

COLORREF CSkinButtonPush::GetBkgndColor()
{
	return CSkinCtrl::GetBkgndColor();
}

void CSkinButtonPush::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	// update if the cursor has moved on or off
	if (!pOldCursor || !pNewCursor)
		Invalidate(FALSE);
}

////

BOOL CSkinButtonGroup::OnPaint(CDC* pDC)
{
	CString strText;
	GetCtrl()->GetWindowText(strText);
	CSize sizeExtent;
	
	pDC->SelectStockObject(ANSI_VAR_FONT);
	sizeExtent = pDC->GetTextExtent("ABC");
	
	CRect rText, rGroup;
	GetClientRect(rText);

	rGroup = rText;
	rGroup.top += sizeExtent.cy / 2;
	
	// draw outline
	Draw3dEdge(pDC, rGroup, min(9, min(rGroup.Height(), rGroup.Width() / 2)), SKCS_ETCHEDEDGE, IM_COLD);
	
	// draw text
	if (!strText.IsEmpty())
	{
		// add a buffer char to front and back
		strText = " " + strText + " ";

		rText.DeflateRect(CORNER, 0);
		UINT nAlign = (GetTextAlignment() & (DT_CENTER | DT_LEFT | DT_RIGHT));
		nAlign |= DT_TOP | DT_SINGLELINE;
		CalcTextRect(pDC, strText, rText, nAlign);
		
		COLORREF colorText = GetColor(IsWindowEnabled() ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
		COLORREF oldcol = pDC->SetTextColor(colorText);

		pDC->SetBkColor(GetBkgndColor());
		pDC->DrawText(strText, rText, nAlign);
		pDC->SetTextColor(oldcol);
	}
	
	return TRUE;
}

COLORREF CSkinButtonGroup::GetBkgndColor()
{
	return GetParentBkgndColor(); 
}

////

BOOL CSkinButtonCheck::OnPaint(CDC* pDC) // return FALSE to allow default painting
{
	CRect rClient, rText, rect;
	GetClientRect(rClient);
	rText = rect = rClient;
	
	UINT nState = GetButton()->GetState();

	BOOL bDown = (nState & 0x0004);
	BOOL bFocus = (nState & 0x0008);
	
	CString strText;
	GetCtrl()->GetWindowText(strText);
	UINT nTextAlign = GetTextAlignment();
	
	CBitmap bmp;
	
	GetClientRect(rClient);
	bmp.CreateCompatibleBitmap(pDC, rClient.Width(), rClient.Height());
	
	CDC dcMem;
	dcMem.CreateCompatibleDC(pDC);
	CBitmap *pOldBitmap = dcMem.SelectObject(&bmp);
	
	dcMem.FillSolidRect(rClient, GetParentBkgndColor());
	
	UINT nStyle = GetStyle();
	int nCheck = GetButton()->GetCheck();
	CRect rButton = GetCheckRect();
		
	if (nStyle & BS_LEFTTEXT)
		rText.right = rButton.left - 3;
	else
		rText.left = rButton.right + 3;
	
	// border
	dcMem.SelectStockObject(NULL_PEN);
	CBrush* pOld = dcMem.SelectObject(GetColorBrush(bDown ? COLOR_3DHILIGHT : COLOR_WINDOW));
	
	dcMem.RoundRect(rButton.left, rButton.top, rButton.right + 1, rButton.bottom + 1, 6, 6);
	dcMem.SelectObject(pOld);

	Draw3dEdge(&dcMem, rButton, 2, SKCS_CLIENTEDGE, GetState(rClient));
		
	// draw state
	if (nCheck)
	{
		dcMem.SetBkMode(TRANSPARENT);
		dcMem.SetTextColor(GetColor(nCheck == 1 ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT));
		
		CFont* pOld = dcMem.SelectObject(GetFont(SBFONT_MARLETT));
		dcMem.DrawText("b", rButton, DT_CENTER | DT_VCENTER);
		dcMem.SelectObject(pOld);
	}
		
	pDC->BitBlt(0, 0, rClient.right, rClient.bottom, &dcMem, 0, 0, SRCCOPY);
	
	// cleanup
	dcMem.SelectObject(pOldBitmap);
	bmp.DeleteObject();
	dcMem.DeleteDC();
	
	// draw the text if there is any
	if (!strText.IsEmpty())
	{
		pDC->SelectStockObject(ANSI_VAR_FONT);
		pDC->SetBkMode(TRANSPARENT);

		rText.DeflateRect(1, 1);
		CalcTextRect(pDC, strText, rText, nTextAlign);

		COLORREF colorText = GetColor(IsWindowEnabled() ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
		COLORREF oldcol = pDC->SetTextColor(colorText);

		pDC->DrawText(strText, rText, nTextAlign | DT_END_ELLIPSIS);
		pDC->SetTextColor(oldcol);
		
		rText.InflateRect(1, 1);
	}
	else
		rText.DeflateRect(CORNER, CORNER);
	
	// Draw the focus
	if (bFocus)
		pDC->DrawFocusRect(rText);
	
	return TRUE;
}

void CSkinButtonCheck::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	// update if the cursor has moved on or off
	if (!pOldCursor || !pNewCursor)
		InvalidateRect(GetHwnd(), GetCheckRect(), FALSE);
}

COLORREF CSkinButtonCheck::GetBkgndColor()
{
	return GetParentBkgndColor(); 
}

////

BOOL CSkinButtonRadio::OnPaint(CDC* pDC) // return FALSE to allow default painting
{
	CRect rClient, rText, rect;
	GetClientRect(rClient);
	rText = rect = rClient;
	
	UINT nState = GetButton()->GetState();

	BOOL bDown = (nState & 0x0004);
	BOOL bFocus = (nState & 0x0008);
	
	CString strText;
	GetCtrl()->GetWindowText(strText);
	UINT nTextAlign = GetTextAlignment();
	
	CBitmap bmp;
	
	GetClientRect(rClient);
	bmp.CreateCompatibleBitmap(pDC, rClient.Width(), rClient.Height());
	
	CDC dcMem;
	dcMem.CreateCompatibleDC(pDC);
	CBitmap *pOldBitmap = dcMem.SelectObject(&bmp);
	
	dcMem.FillSolidRect(rClient, GetParentBkgndColor());
	
	UINT nStyle = GetStyle();
	int nCheck = GetButton()->GetCheck();
		
	CRect rButton = GetCheckRect();
	
	// border
	dcMem.SelectStockObject(NULL_PEN);
	CBrush* pOld = dcMem.SelectObject(GetColorBrush(bDown ? COLOR_3DHILIGHT : COLOR_WINDOW));
	
	dcMem.Ellipse(rButton.left, rButton.top, rButton.right + 1, rButton.bottom + 1);
	dcMem.SelectObject(pOld);

	Draw3dEdge(&dcMem, rButton, rButton.Height() / 2, SKCS_CLIENTEDGE, GetState(rClient));
	
	// draw state
	if (nCheck > 0)
	{
		dcMem.SetBkMode(TRANSPARENT);
		dcMem.SetTextColor(GetColor(nCheck == 1 ? COLOR_WINDOWTEXT : COLOR_3DFACE));
		
		CFont* pOld = dcMem.SelectObject(GetFont(SBFONT_MARLETT));
		dcMem.DrawText("i", rButton, DT_CENTER | DT_VCENTER);
		dcMem.SelectObject(pOld);
	}
	
	pDC->BitBlt(0, 0, rClient.right, rClient.bottom, &dcMem, 0, 0, SRCCOPY);

	// cleanup
	dcMem.SelectObject(pOldBitmap);
	bmp.DeleteObject();
	dcMem.DeleteDC();

	// draw the text if there is any
	if (nStyle & BS_LEFTTEXT)
		rText.right = rButton.left - 4;
	else
		rText.left = rButton.right + 4;
	
	if (!strText.IsEmpty())
	{
		pDC->SelectStockObject(ANSI_VAR_FONT);
		pDC->SetBkMode(TRANSPARENT);
		
		rText.DeflateRect(1, 1);
		CalcTextRect(pDC, strText, rText, nTextAlign);

		COLORREF colorText = GetColor(IsWindowEnabled() ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
		COLORREF oldcol = pDC->SetTextColor(colorText);

		pDC->DrawText(strText, rText, nTextAlign | DT_END_ELLIPSIS);
		pDC->SetTextColor(oldcol);
		
		rText.InflateRect(1, 1);
	}
	else
		rText.DeflateRect(CORNER, CORNER);

	// Draw the focus circle on the inside of the button if it is non-stretched
	if (bFocus)
		pDC->DrawFocusRect(rText);

	return TRUE;
}

void CSkinButtonRadio::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	// update if the cursor has moved on or off
	if (!pOldCursor || !pNewCursor)
		InvalidateRect(GetHwnd(), GetCheckRect(), FALSE);
}

COLORREF CSkinButtonRadio::GetBkgndColor()
{
	return GetParentBkgndColor(); 
}

//////////////////////////////////////////////////////////////////////////////////////////////////

COLORREF CSkinCombolbox::GetParentBkgndColor()
{
	HWND hParent = GetParent();
	ASSERT (hParent);
	
	CSkinCtrl* pSkin = GetSkinCtrl(hParent);
	
	if (pSkin && CWinClasses::IsClass(hParent, WC_COMBOBOX))
	{
		DWORD dwStyle = ::GetWindowLong(hParent, GWL_STYLE);
		
		if ((dwStyle & 0xf) == CBS_SIMPLE)
		{
			return pSkin->GetParentBkgndColor();
		}
	}
	
	// else
	return CSkinCtrl::GetParentBkgndColor();
}

void CSkinCombolbox::DrawScrollbars(CDC* pDC, CRect rect, int nBar)
{
	rect.InflateRect(EDGE, EDGE);

	if (!IsPopup())
	{
		rect.right -= 2;
	}
	
	CSkinListbox::DrawScrollbars(pDC, rect, nBar);
}

void CSkinCombolbox::OnNcCalcSize(LPRECT pClient)
{
	pClient->right -= 2;
}

void CSkinCombolbox::OnNcPaint(CDC* pDC)
{
	CRect rWindow;
	GetDrawRect(rWindow);
	
	rWindow.right--;
	NcPaintBorder(pDC, rWindow, GetParentBkgndColor(), GetBkgndColor(), EdgeStyle());
	rWindow.right++;

	DrawScrollbars(pDC, rWindow);
	
	rWindow.right--;
	rWindow.top--;
	Draw3dEdge(pDC, rWindow, CORNER, SKCS_CLIENTEDGE, GetState(rWindow));
}

//////////////////////////////////////////////////////////////////////////////////////////////////

int CSkinHeader::HitTest(POINT ptClient)
{
	CRect rClient, rParent;
	GetClientRect(rClient);

	CHeaderCtrl* pHeader = GetHeader();
	CWnd* pParent = pHeader->GetParent();

	pParent->GetClientRect(rParent);
	pParent->MapWindowPoints(pHeader, rParent);

	rClient.right = min(rParent.right, rClient.right);
	rClient.left = max(rParent.left, rClient.left);

	if (!rClient.PtInRect(ptClient))
		return -1;

	// else
	HDHITTESTINFO hhti;
	hhti.pt = ptClient;

	return SendMessage(HDM_HITTEST, 0, (LPARAM)&hhti);
}

BOOL CSkinHeader::OnPaint(CDC* pDC)
{
	// we do all the drawing ourselves
	CHeaderCtrl* pHeader = GetHeader();
	CRect rClient;
	GetClientRect(rClient);

	CRect rParent;
	CWnd* pParent = pHeader->GetParent();

	pParent->GetClientRect(rParent);
	pParent->MapWindowPoints(pHeader, rParent);

	rClient.right = min(rParent.right + 2, rClient.right);
	rClient.left = max(rParent.left, rClient.left);

	BOOL bMouseDown = !m_bDragging && IsHot() && (GetAsyncKeyState(MK_LBUTTON) & 0x8000);
	BOOL bTextured = (GetControlBitmap(SKCB_HEADERLEFT) != NULL);

	CDC dcMem;
	dcMem.CreateCompatibleDC(pDC);
	
	CBitmap bmp;
	bmp.CreateCompatibleBitmap(pDC, rClient.Width(), rClient.Height());
	CBitmap* pOldBM = dcMem.SelectObject(&bmp);
	
	CRect rMem(rClient);
	rMem.OffsetRect(-rClient.TopLeft());
	dcMem.FillSolidRect(rMem, GetParentBkgndColor());
	rMem.right -= 2;

	int nCount = pHeader->GetItemCount();

	dcMem.SelectStockObject(ANSI_VAR_FONT);
	dcMem.SetTextColor(GetColor(COLOR_WINDOWTEXT));
	dcMem.SetBkMode(TRANSPARENT);

	CImageList* pIL = pHeader->GetImageList();

	CPoint ptCursor;
	GetCursorPos(&ptCursor);

	int nHotItem = HitTest(ptCursor);
	int nItem = nCount;

	while (nItem--)
	{
		CRect rItem;
		pHeader->GetItemRect(nItem, rItem);

		if (rItem.right <= rClient.left || rItem.left >= rClient.right)
			continue;

		rItem.OffsetRect(-rClient.TopLeft());

		// else
		CString sText;
		HDITEM hdi;
		hdi.mask = HDI_TEXT | HDI_IMAGE | HDI_FORMAT;
		hdi.pszText = sText.GetBuffer(100);
		hdi.cchTextMax = 99;
		
		VERIFY (pHeader->GetItem(nItem, &hdi));
		sText.ReleaseBuffer();
		
		BOOL bDown = (!m_bDragging && bMouseDown && (nItem == nHotItem));
		BOOL bHot = (!m_bDragging && !bMouseDown && (nItem == nHotItem));

		BOOL bLeftEnd = (rItem.left == rMem.left);
		BOOL bRightEnd = (rItem.right >= rMem.right - 2);

		int nState = !IsWindowEnabled() ? IM_DISABLED : (bDown ? IM_DOWN : bHot ? IM_HOT : IM_COLD);

		CRect rOrgItem(rItem); // save
		rItem.left = max(rItem.left, rMem.left);
		rItem.right = min(rItem.right, rMem.right);

		if (bTextured)
		{
			COLORREF crMask;
			CBitmap* pMid = GetControlBitmap(SKCB_HEADERMIDDLE, nState, &crMask);
			CBitmap* pLeft = !bLeftEnd ? pMid : GetControlBitmap(bLeftEnd ? SKCB_HEADERLEFTEND : SKCB_HEADERLEFT, nState, NULL, SKCB_HEADERLEFT);
			CBitmap* pRight = !bRightEnd ? pMid : GetControlBitmap(bRightEnd ? SKCB_HEADERRIGHTEND : SKCB_HEADERRIGHT, nState, NULL, SKCB_HEADERRIGHT);

			DrawPushButton(&dcMem, rItem, pLeft, pMid, pRight, -1, crMask);

			// draw a divider
			if (!bRightEnd)
				dcMem.FillSolidRect(rItem.right - 1, rItem.top, 1, rItem.Height(), GetColor(COLOR_3DSHADOW));
		}
		else
		{
			DrawPushButton(&dcMem, rItem, nState, CORNER, -1, !bLeftEnd, !bRightEnd);
		}

		rItem = rOrgItem; // restore
		rItem.DeflateRect(4, 0);

		if (!sText.IsEmpty())
		{
			CalcTextRect(&dcMem, sText, rItem, DT_LEFT | DT_VCENTER);

			if (bDown)
				rItem.OffsetRect(1, 1);

			COLORREF colorText = GetColor(IsWindowEnabled() ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
			dcMem.SetTextColor(colorText);

			dcMem.DrawText(sText, rItem, DT_LEFT | DT_TOP | DT_END_ELLIPSIS);
		}
	}
	
	pDC->BitBlt(rClient.left, 0, rClient.right, rClient.bottom, &dcMem, 0, 0, SRCCOPY);
	
	dcMem.SelectObject(pOldBM);
	dcMem.DeleteDC();
	bmp.DeleteObject();
	
	return TRUE;
}

void CSkinHeader::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	if (m_bDragging)
		return;

	CHeaderCtrl* pHeader = GetHeader();
	int nOldItem = -1;
	int nNewItem = -1;

	if (pOldCursor && pNewCursor)
	{
		nOldItem = HitTest(*pOldCursor);
		nNewItem = HitTest(*pNewCursor);
	}
	else if (pOldCursor)
	{
		nOldItem = HitTest(*pOldCursor);
	}
	else if (pNewCursor)
	{
		nNewItem = HitTest(*pNewCursor);
	}
	
	if (nOldItem != -1 || nNewItem != -1)
		Invalidate(FALSE);
}

LRESULT CSkinHeader::OnNotifyReflect(NMHDR* pNMHDR, LRESULT lrParent)
{
	switch (pNMHDR->code)
	{
	case HDN_TRACK:
	case HDN_ITEMCHANGED:
	case 0xfffffeb8://HDN_TRACK:
	case 0xfffffebf://HDN_ITEMCHANGED:
		{
			CWnd* pHeader = GetHeader();
			pHeader->Invalidate();
			break;
		}

	case HDN_BEGINTRACK:
	case HDN_BEGINDRAG:
	case 0xfffffeba://HDN_BEGINTRACK:
		if (!lrParent)
			m_bDragging = TRUE;
		break;

	case HDN_ENDTRACK:
	case HDN_ENDDRAG:
	case 0xfffffeb9://HDN_ENDTRACK:
		m_bDragging = FALSE;
		break;
	}
	
	return CSkinCtrl::OnNotifyReflect(pNMHDR, lrParent);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinMonthcal::AttachWindow(CWnd* pCtrl)
{
	// must change this prior to calling base class
	if (!(pCtrl->GetExStyle() & WS_EX_CLIENTEDGE))
	{
		if (IsPopup())
			m_dwBaseStyle &= ~SKCS_EDGEMASK;
		else
			m_dwBaseStyle = 0;
	}

	// enlarge to take account of border
	if (CSkinCtrl::AttachWindow(pCtrl))
	{
		if (!IsPopup() && EdgeStyle() != SKCS_NOEDGE)
		{
			CRect rCtrl;
			pCtrl->GetWindowRect(rCtrl);
			pCtrl->ScreenToClient(rCtrl);
			pCtrl->MapWindowPoints(pCtrl->GetParent(), rCtrl);

			rCtrl.right += 4;
			rCtrl.bottom += 4;
			pCtrl->MoveWindow(rCtrl);
		}		
		return TRUE;
	}
	
	return FALSE;
}

void CSkinMonthcal::OnNcCalcSize(LPRECT pClient)
{
	if (EdgeStyle() != SKCS_NOEDGE)
		::InflateRect(pClient, -EDGE - 2, -EDGE - 2);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

void CSkinTabcontrol::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	CSkinCtrl::OnUpdateHotspots(pOldCursor, pNewCursor, uOldHitTest, uNewHitTest);

	// redraw the border and selected item if we've moved on or off
	if ((pOldCursor || pNewCursor) && !(pOldCursor && pNewCursor))
	{
		Invalidate(FALSE);
	}
}

BOOL CSkinTabcontrol::OnEraseBkgnd(CDC* pDC) 
{
	CRect rClient, rTab, rTotalTab, rBkgnd, rEdge;
	COLORREF crBack;
	int nTab, nTabHeight = 0;
	
	// calc total tab width
	GetClientRect(rClient);
	nTab = GetTab()->GetItemCount();
	rTotalTab.SetRectEmpty();
	
	while (nTab--)
	{
		GetTab()->GetItemRect(nTab, rTab);
		rTotalTab.UnionRect(rTab, rTotalTab);
	}
	
	nTabHeight = rTotalTab.Height();
	
	// add a bit
	rTotalTab.InflateRect(2, 3);
	rEdge = rTotalTab;
	
	// then if background color is set, paint the visible background
	// area of the tabs in the bkgnd color
	// note: the mfc code for drawing the tabs makes all sorts of assumptions
	// about the background color of the tab control being the same as the page
	// color - in some places the background color shows thru' the pages!!
	// so we must only paint the background color where we need to, which is that
	// portion of the tab area not excluded by the tabs themselves
	crBack = GetParentBkgndColor();
	
	// full width of tab ctrl above top of tabs
	rBkgnd = rClient;
	rBkgnd.bottom = rTotalTab.top + 3;
	pDC->FillSolidRect(rBkgnd, crBack);
	
	// width of tab ctrl visible bkgnd including bottom pixel of tabs to left of tabs
	rBkgnd = rClient;
	rBkgnd.right = 2;
	rBkgnd.bottom = rBkgnd.top + (nTabHeight + 2);
	pDC->FillSolidRect(rBkgnd, crBack);
	
	// to right of tabs
	rBkgnd = rClient;
	rBkgnd.left += rTotalTab.Width() - 2;
	rBkgnd.bottom = rBkgnd.top + (nTabHeight + 2);
	pDC->FillSolidRect(rBkgnd, crBack);
	
	OnPaint(pDC);
	
	return TRUE;
}

void CSkinTabcontrol::OnNcPaint(CDC* pDC) 
{
	CRect rWindow, rClient;
	GetDrawRect(rWindow, rClient);

	BOOL bHot = IsHot(rWindow);

	// draw the rest of the border
	CRect rPage;
	GetClientRect(rPage);
	GetTab()->AdjustRect(FALSE, rPage);
	rPage.top -= 2;
	rClient.top += rPage.top;
	
	COLORREF crBkgnd = GetParentBkgndColor();
	
	pDC->FillSolidRect(rWindow.left, rWindow.top, EDGE, rClient.top - rWindow.top, crBkgnd);
	pDC->FillSolidRect(rWindow.left, rWindow.top, rWindow.right - rWindow.left, EDGE, crBkgnd);
	pDC->FillSolidRect(rWindow.right - EDGE, rWindow.top, EDGE, rClient.top - rWindow.top, crBkgnd);
	
	rWindow.top = rClient.top;

	Draw3dEdge(pDC, rWindow, 4, SKCS_BTNEDGE, bHot ? IM_HOT : IM_COLD);

	// rub out the base line of the selected tab if enough of the tab is visible
	int nSel = GetTab()->GetCurSel();

	if (nSel != -1)
	{
		CRect rTab;
		GetTab()->GetItemRect(nSel, rTab);

		if (rTab.right > 4)
		{
			ClientToWindow(rTab);
			pDC->FillSolidRect(rTab.left + 2, rWindow.top, rTab.Width() - 2, 2, GetBkgndColor());
		}
	}
}

BOOL CSkinTabcontrol::IsHot(LPRECT pRect)
{
	if (CSkinCtrl::IsHot(pRect))
		return TRUE;

	// else check if spin button has the capture
	CWnd* pSpin = GetChildWnd(WC_SPIN);
		
	return (pSpin && (pSpin->GetSafeHwnd() == GetCapture()));
}

BOOL CSkinTabcontrol::OnPaint(CDC* pDC) 
{
	// prepare dc
	pDC->SelectObject(GetTab()->GetFont());

	// clip out the spinctrl and bit more
	CWnd* pSpin = GetChildWnd(WC_SPIN);

	if (pSpin && pSpin->IsWindowVisible())
	{
		CRect rSpin;
		pSpin->GetWindowRect(rSpin);
		GetCtrl()->ScreenToClient(rSpin);
		rSpin.InflateRect(2, 2);

		pDC->ExcludeClipRect(rSpin);
	}
	
	// paint the tabs first and then the borders
	int nTab = GetTab()->GetItemCount();
	int nSel = GetTab()->GetCurSel();
	
	if (!nTab) // no pages added
		return TRUE;
	
	CRect rPage, rClient;
	GetClientRect(rClient);
	GetTab()->AdjustRect(FALSE, rClient);
	rClient.top -= 2;
	
	while (nTab--)
	{
		if (nTab != nSel)
		{
			VERIFY(GetTab()->GetItemRect(nTab, rPage));
			
			// if there is not enough of the tab showing then don't paint it
			if (rPage.right > 3)
			{
				DrawItem(pDC, nTab, rPage);
				DrawItemBorder(pDC, nTab, rPage, nSel);
			}
		}
	}
	
	// now selected tab
	VERIFY(GetTab()->GetItemRect(nSel, rPage));
	
	if (rPage.right > 3)
	{
		rPage.bottom++;
		rPage.top -= 2;
		
		DrawItem(pDC, nSel, rPage, TRUE);
		DrawItemBorder(pDC, nSel, rPage, nSel);
	}

	SendMessage(WM_NCPAINT); // to redraw the main border
	
	return TRUE;
}

void CSkinTabcontrol::DrawItem(CDC* pDC, int nItem, LPRECT pRect, BOOL bSelected)
{
	TC_ITEM     tci;
	CImageList* pilTabs = GetTab()->GetImageList();
	const int PADDING = 2;
	
	CRect rItem(pRect);

	// tab
	pDC->FillSolidRect(rItem, GetBkgndColor());
	
	// text & icon
	rItem.left += PADDING;
	rItem.top += PADDING + (bSelected ? 1 : 0);
	
	pDC->SetBkMode(TRANSPARENT);
	
	CString sTemp;
	tci.mask        = TCIF_TEXT | TCIF_IMAGE;
	tci.pszText     = sTemp.GetBuffer(100);
	tci.cchTextMax  = 99;
	GetTab()->GetItem(nItem, &tci);
	sTemp.ReleaseBuffer();
	
	// icon
	if (pilTabs)
	{
		pilTabs->Draw(pDC, tci.iImage, CPoint(rItem.left, rItem.top), ILD_TRANSPARENT);
		rItem.left += 16 + PADDING;
	}
	
	// text
	rItem.right -= PADDING;
	
	COLORREF colorText = GetColor(IsWindowEnabled() ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
	pDC->SetTextColor(colorText);

	pDC->DrawText(sTemp, rItem, DT_NOPREFIX | DT_CENTER | DT_END_ELLIPSIS);
}

void CSkinTabcontrol::DrawItemBorder(CDC* pDC, int nItem, LPRECT pRect, int nSelItem)
{
	COLORREF crHighlight = GetColor(COLOR_3DHILIGHT);
	COLORREF crShadow = GetColor(COLOR_3DSHADOW);
	
	if (nItem == nSelItem)
	{
		Draw3dEdge(pDC, pRect, 4, SKCS_BTNEDGE, IsHot() ? IM_HOT : IM_COLD, ISKCR_CLIPBOTTOM);
	}
	else // draw simple dividers
	{
		// only draw the left divider if its the first item or the previous item is not visible
		if (nItem == 0 || pRect->left < 4)
			pDC->FillSolidRect(pRect->left, pRect->top, 1, pRect->bottom - pRect->top - 1, crShadow);

		// don't draw the right divider if we preceed the selected item
		if (nItem != nSelItem - 1)
			pDC->FillSolidRect(pRect->right - 1, pRect->top, 1, pRect->bottom - pRect->top - 1, crShadow);
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

LRESULT CSkinRebar::OnCustomDraw(NMCUSTOMDRAW* pNMCD, DWORD dwStage)
{
	switch (dwStage)
	{
	case CDDS_ITEMPREPAINT:
		{
			CDC* pDC = CDC::FromHandle(pNMCD->hdc);
		}
	}
	// default
	return CDRF_DODEFAULT;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinTooltip::AttachWindow(CWnd* pCtrl)
{
	// don't ever hook these windows just setup the colors and return
	pCtrl->SendMessage(TTM_SETTIPBKCOLOR, (WPARAM)GetColor(COLOR_INFOBK), 0);
	pCtrl->SendMessage(TTM_SETTIPTEXTCOLOR, (WPARAM)GetColor(COLOR_INFOTEXT), 0);

	return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

void CSkinTrackbar::OnUpdateHotspots(LPPOINT pOldCursor, LPPOINT pNewCursor, UINT uOldHitTest, UINT uNewHitTest)
{
	// only update if the cursor has moved on or off the thumb
	CRect rThumb;
	GetSlider()->GetThumbRect(rThumb);

	BOOL bWasOn = FALSE, bOn = FALSE;
	
	if (pOldCursor && pNewCursor)
	{
		bWasOn = rThumb.PtInRect(*pOldCursor);
		bOn = rThumb.PtInRect(*pNewCursor);
	}
	else if (pOldCursor)
		bWasOn = rThumb.PtInRect(*pOldCursor);

	else if (pNewCursor)
		bOn = rThumb.PtInRect(*pNewCursor);
	
	if (bWasOn != bOn)
		Refresh();
}
/*
BOOL CSkinTrackbar::OnPaint(CDC* pDC)
{
	CRect rThumb, rChannel, rClient;

	GetSlider()->GetClientRect(rClient);
	GetSlider()->GetThumbRect(rThumb);
	GetSlider()->GetChannelRect(rChannel);

	BOOL bHorz = !(GetStyle() & TBS_VERT);

	// GetChannelRect bug
	if (!bHorz)
	{
		CRect rTemp(rChannel);

		rChannel.left = rTemp.top;
		rChannel.right = rTemp.bottom;
		rChannel.top = rTemp.left;
		rChannel.bottom = rTemp.right;
	}

	// clipping
	rThumb.IntersectRect(rThumb, rClient);

	int nState = m_bDragging ? IM_DOWN : (IsHot(rThumb) ? IM_HOT : IM_COLD);

	// fill bkgnd and draw channel
	pDC->FillSolidRect(rClient, GetParentBkgndColor());
	pDC->Draw3dRect(rChannel, GetColor(COLOR_3DSHADOW), GetColor(COLOR_3DHILIGHT));
				
	COLORREF crMask;
	CBitmap* pThumb = GetControlBitmap(bHorz ? SKCB_SLIDERTHUMBHORZ : SKCB_SLIDERTHUMBVERT, nState, &crMask);
				
	if (pThumb)
	{
		BITMAP bm;
		pThumb->GetBitmap(&bm);
					
		CDC dcMem;
		dcMem.CreateCompatibleDC(pDC);
		CBitmap *pOldBitmap = dcMem.SelectObject(pThumb);
					
		CSkinBase::StretchBlt(pDC, rThumb.left, rThumb.top, rThumb.Width(), rThumb.Height(),
							&dcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY, crMask);
	
		dcMem.SelectObject(pOldBitmap);
	}
	else
	{
		COLORREF color = GetColor((nState == IM_DOWN || nState == IM_HOT) ? COLOR_3DHILIGHT : COLOR_3DFACE);
					
		pDC->FillSolidRect(rThumb, color);
		NcPaintBorder(pDC, rThumb, GetParentBkgndColor(), color, SKCS_BTNEDGE, FALSE);
	}

	return TRUE;
}
*/

LRESULT CSkinTrackbar::OnCustomDraw(NMCUSTOMDRAW* pNMCD, DWORD dwStage)
{
	switch (dwStage)
	{
	case CDDS_ITEMPREPAINT:
		{
			if (pNMCD->dwItemSpec == TBCD_CHANNEL)
			{
				CDC* pDC = CDC::FromHandle(pNMCD->hdc);
				CRect rClient;
				GetClientRect(rClient);
				
				pDC->Draw3dRect(&pNMCD->rc, GetColor(COLOR_3DSHADOW), GetColor(COLOR_3DHILIGHT));
				
				return CDRF_SKIPDEFAULT;
			}
			else if (pNMCD->dwItemSpec == TBCD_THUMB)
			{
				CDC* pDC = CDC::FromHandle(pNMCD->hdc);
				CRect rThumb(pNMCD->rc);

				// clip thumb to client rect
				CRect rClient;
				GetClientRect(rClient);
				rThumb.IntersectRect(rThumb, rClient);

				int nState = m_bDragging ? IM_HOT : (IsHot(rThumb) ? IM_HOT : IM_COLD);
				BOOL bHorz = (GetStyle() & TBS_HORZ);

				COLORREF crMask;
				CBitmap* pThumb = GetControlBitmap(bHorz ? SKCB_SLIDERTHUMBHORZ : SKCB_SLIDERTHUMBVERT, nState, &crMask);

				if (pThumb)
				{
					BITMAP bm;
					pThumb->GetBitmap(&bm);

					CDC dcMem;
					dcMem.CreateCompatibleDC(pDC);
					CBitmap *pOldBitmap = dcMem.SelectObject(pThumb);

					CSkinBase::StretchBlt(pDC, rThumb.left, rThumb.top, rThumb.Width(), rThumb.Height(),
											&dcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY, crMask);

					dcMem.SelectObject(pOldBitmap);
				}
				else
				{
					COLORREF color = GetColor((nState == IM_DOWN || nState == IM_HOT) ? COLOR_3DHILIGHT : COLOR_3DFACE);

//					pDC->FillSolidRect(rThumb, color);
					DrawPushButton(pDC, rThumb, nState, min(rThumb.Height(), rThumb.Width()) / 2, GetParentBkgndColor());
				}

				return CDRF_SKIPDEFAULT;
			}
		}
	}
	// default
	return CDRF_DODEFAULT;
}

void CSkinTrackbar::OnScroll(UINT nSBCode, UINT nPos, BOOL bHorz)
{
	switch (nSBCode)
	{
	case TB_THUMBTRACK:
		m_bDragging = TRUE;
		break;

	default:
		m_bDragging = FALSE;
		break;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////

LRESULT CSkinSHDLLDefView::OnMsg(UINT msg, WPARAM wp, LPARAM lp)
{
	UINT uRes = 0;
	LRESULT lr = 0;

	switch (msg) 
	{
	case WM_NOTIFY:
		{
			NMHDR* pNMHDR = (NMHDR*)lp;
			
			if (pNMHDR->code == NM_CUSTOMDRAW)
				return CSkinCtrl::OnMsg(msg, wp, lp);
			else
				return Default();
		}
		break;
	}

	// We don't handle it: pass along
	return CSkinCtrl::OnMsg(msg, wp, lp);
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CSkinOther::OnEraseBkgnd(CDC* pDC)
{
	return CSkinCtrl::OnEraseBkgnd(pDC);
}

BOOL CSkinOther::AttachWindow(CWnd* pCtrl)
{
	// make an educated guess as to the edge style
	if (pCtrl->GetExStyle() & WS_EX_CLIENTEDGE)
		m_dwBaseStyle |= SKCS_CLIENTEDGE;

	m_dwBaseStyle |= SKCS_NCPAINT;

	return CSkinCtrl::AttachWindow(pCtrl);
}

void CSkinOther::OnNcPaint(CDC* pDC)
{
	CSkinCtrl::OnNcPaint(pDC);
}